package com.gitee.loyo.controller;

import com.gitee.loyo.Endpoints;
import com.gitee.loyo.Global;
import com.gitee.loyo.Pages;
import com.gitee.loyo.page.EndpointPage;
import com.gitee.loyo.utils.HttpUtils;
import com.gitee.loyo.utils.NetUtils;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServerRequest;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.*;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Map;

public class EndpointController {
    private final Logger log = LoggerFactory.getLogger(EndpointController.class);

    @GET
    @Path("/endpoint")
    public void getEndpoints(HttpServerRequest request, Promise<Void> promise){
        EndpointPage hostsPage = new EndpointPage();
        hostsPage.proxies = new ArrayList<>();
        hostsPage.clients = new ArrayList<>();
        for (Map.Entry<String, Endpoints.Endpoint> entry : Endpoints.endpoints.entrySet()) {
            if(entry.getValue() instanceof Endpoints.Client){
                hostsPage.clients.add((Endpoints.Client) entry.getValue());
            }else if(entry.getValue() instanceof Endpoints.Proxy){
                hostsPage.proxies.add((Endpoints.Proxy) entry.getValue());
            }
        }

        Template tmpl = Global.VELOCITY_ENGINE.getTemplate("vm/endpoint.vm");
        VelocityContext ctx = new VelocityContext();
        ctx.put("page", hostsPage);
        StringWriter output = new StringWriter();
        tmpl.merge(ctx, output);
        Pages.renderPage(request.response(), output.toString());
        promise.complete();
    }

    @POST
    @Path("/proxy")
    public void saveProxy(HttpServerRequest request, Promise<Void> promise){
        Future.<Endpoints.Proxy>future(p ->
            HttpUtils.getBodyAsJson(request, Endpoints.Proxy.class, p)
        ).compose(proxy -> {
            if(proxy.id == null || proxy.id.isEmpty()){
                return Future.failedFuture("proxy id is empty");
            }
            return Endpoints.addProxy(proxy);
        }).onComplete(ar -> completion(ar, request, promise));
    }

    @PUT
    @Path("/proxy")
    public void updateProxy(HttpServerRequest request, Promise<Void> promise){
        Future.<Endpoints.Proxy>future(p ->
            HttpUtils.getBodyAsJson(request, Endpoints.Proxy.class, p)
        ).compose(proxy -> {
            if(proxy.id == null || proxy.id.isEmpty()){
                return Future.failedFuture("proxy id is empty");
            }
            return Endpoints.updateProxy(proxy);
        }).onComplete(ar -> completion(ar, request, promise));
    }

    @DELETE
    @Path("/proxy")
    public void deleteProxy(HttpServerRequest request, Promise<Void> promise){
        Future.<Endpoints.Proxy>future(p ->
            HttpUtils.getBodyAsJson(request, Endpoints.Proxy.class, p)
        ).compose(proxy -> {
            if(proxy.id == null || proxy.id.isEmpty()){
                return Future.failedFuture("proxy id is empty");
            }
            return Endpoints.removeProxy(proxy);
        }).onComplete(ar -> completion(ar, request, promise));
    }

    @HEAD
    @Path("/proxy")
    public void pingProxy(HttpServerRequest request, Promise<Void> promise){
        Future.<Void>future(p -> {
            String id = request.getParam("id");
            if(id == null || id.isEmpty()){
                p.fail("proxy id is empty");
                return;
            }
            Endpoints.Endpoint endpoint = Endpoints.endpoints.get(id);
            if(endpoint == null || !(endpoint instanceof Endpoints.Proxy)){
                p.fail("failed");
                return;
            }

            NetUtils.ping(((Endpoints.Proxy) endpoint).host, ((Endpoints.Proxy) endpoint).port).onComplete(p);
        }).onComplete(ar -> {
            if(ar.failed()){
                log.error("ping error", ar.cause());
                request.response().setStatusCode(400).end();
            }else{
                request.response().setStatusCode(204).end();
            }
            promise.complete();
        });
    }

    private void completion(AsyncResult<Void> ar, HttpServerRequest request, Promise<Void> promise){
        if(ar.failed()){
            ar.cause().printStackTrace();
            request.response().setStatusCode(400).end(ar.cause().getMessage());
        }else{
            request.response().setStatusCode(200).end();
        }
        promise.complete();
    }
}
